/*
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "camera_kit.h"
#include "recorder.h"
#include "camera_sample.h"
#include "rtsp_log.h"
#include "securec.h"

#include <algorithm>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

//#define  O_CREAT     0100
//#define  O_RDWR      0002
//#define  S_IRUSR     00400
//#define  S_IWUSR     00200


using namespace std;
using namespace OHOS;
using namespace OHOS::Media;

static int32_t SampleGetRecordFd()
{
    struct timeval tv = {};
    gettimeofday(&tv, nullptr);
    struct tm *ltm = localtime(&tv.tv_sec);
    int32_t fd = -1;
    if (ltm != nullptr) {
        ostringstream ss("Capture_");
        ss << "Record" << ltm->tm_hour << "-" << ltm->tm_min << "-" << ltm->tm_sec << ".mp4";

        cout << "Open "
             << "/sdcard/" << ss.str() << endl;

        fd = open(("/sdcard/" + ss.str()).c_str(), O_RDWR | O_CREAT | O_CLOEXEC | O_TRUNC, S_IROTH | S_IRUSR | S_IWUSR);

        if (fd == -1) {
            cout << "Open recorder file failed. strerr=" << strerror(errno) << endl;
            SAMPLE_ERROR("Open recorder file failed. strerr");
        }
        fd = open(("/storage/" + ss.str()).c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);


        cout << "Open "
             << "/storage/" << ss.str() << endl;

        if (fd == -1) {
            cout << "Open recorder file failed. strerr=" << strerror(errno) << endl;
            SAMPLE_ERROR("Open recorder file failed. strerr");
        }
    }
    return fd;
}

static void SampleSaveCapture(const char *p, uint32_t size)
{
    cout << "Start saving picture" << endl;
    struct timeval tv = {};
    gettimeofday(&tv, nullptr);
    struct tm *ltm = localtime(&tv.tv_sec);
    if (ltm != nullptr) {
        ostringstream ss("Capture_");
        ss << "Capture" << ltm->tm_hour << "-" << ltm->tm_min << "-" << ltm->tm_sec << ".jpg";

        ofstream pic("/sdcard/" + ss.str(), ofstream::out | ofstream::trunc);
        cout << "write " << size << " bytes" << endl;
        pic.write(p, size);
        pic.close();
        cout << "Saving picture end" << endl;
    }
}

Recorder *SampleCreateRecorder()
{
    int ret = 0;
    int32_t sampleRate = 48000;
    int32_t channelCount = 1;
    AudioCodecFormat audioFormat = AAC_LC;
    AudioSourceType inputSource = AUDIO_MIC;
    int32_t audioEncodingBitRate = sampleRate;
    VideoSourceType source = VIDEO_SOURCE_SURFACE_ES;
    int32_t frameRate = 30;
    double fps = 30;
    int32_t rate = 4096;
    int32_t sourceId = 0;
    int32_t audioSourceId = 0;
    int32_t width = 1920;
    int32_t height = 1080;
    VideoCodecFormat encoder;
    encoder = HEVC;
    Recorder *recorder = new Recorder();
    if ((ret = recorder->SetVideoSource(source, sourceId)) != SUCCESS) {
        SAMPLE_ERROR("SetVideoSource failed. ret ->%d",ret);
        goto ERROR;
    }
    if ((ret = recorder->SetVideoEncoder(sourceId, encoder)) != SUCCESS) {
        SAMPLE_ERROR("SetVideoEncoder failed. ret ->%d",ret);
        goto ERROR;
    }
    if ((ret = recorder->SetVideoSize(sourceId, width, height)) != SUCCESS) {
        SAMPLE_ERROR("SetVideoSize failed. ret ->%d",ret);
        goto ERROR;
    }
    if ((ret = recorder->SetVideoFrameRate(sourceId, frameRate)) != SUCCESS) {
        SAMPLE_ERROR("SetVideoFrameRate failed. ret ->%d",ret);
        goto ERROR;
    }
    if ((ret = recorder->SetVideoEncodingBitRate(sourceId, rate)) != SUCCESS) {
        SAMPLE_ERROR("SetVideoEncodingBitRate failed. ret ->%d",ret);
        goto ERROR;
    }
    if ((ret = recorder->SetCaptureRate(sourceId, fps)) != SUCCESS) {
        SAMPLE_ERROR("SetCaptureRate failed. ret ->%d",ret);
        goto ERROR;
    }
    if ((ret = recorder->SetAudioSource(inputSource, audioSourceId)) != SUCCESS) {
        SAMPLE_ERROR("SetAudioSource failed. ret ->%d",ret);        
        goto ERROR;
    }
    if ((ret = recorder->SetAudioEncoder(audioSourceId, audioFormat)) != SUCCESS) {
        SAMPLE_ERROR("SetAudioEncoder failed. ret ->%d",ret);        
        goto ERROR;
    }
    if ((ret = recorder->SetAudioSampleRate(audioSourceId, sampleRate)) != SUCCESS) {
        SAMPLE_ERROR("SetAudioSampleRate failed. ret ->%d",ret);
        goto ERROR;
    }
    if ((ret = recorder->SetAudioChannels(audioSourceId, channelCount)) != SUCCESS) {
        SAMPLE_ERROR("SetAudioChannels failed. ret ->%d",ret);
        goto ERROR;
    }
    if ((ret = recorder->SetAudioEncodingBitRate(audioSourceId, audioEncodingBitRate)) != SUCCESS) {
        SAMPLE_ERROR("SetAudioEncodingBitRate failed. ret ->%d",ret);
        goto ERROR;
    }
    if ((ret = recorder->SetMaxDuration(36000)) != SUCCESS) { // 36000s=10h
        SAMPLE_ERROR("SetMaxDuration failed. ret ->%d",ret);
        goto ERROR;
    }
    return recorder;

ERROR:
    delete recorder;
    return nullptr;
}

void SampleFrameStateCallback::OnFrameFinished(Camera &camera, FrameConfig &fc, FrameResult &result)
    {
        SAMPLE_INFO("Receive frame complete inform.");
        if (fc.GetFrameConfigType() == FRAME_CONFIG_CAPTURE) {
            SAMPLE_INFO("Capture frame received.");
            list<Surface *> surfaceList = fc.GetSurfaces();
            for (Surface *surface : surfaceList) {
                SurfaceBuffer *buffer = surface->AcquireBuffer();
                if (buffer != nullptr) {
                    char *virtAddr = static_cast<char *>(buffer->GetVirAddr());
                    if (virtAddr != nullptr) {
                        SampleSaveCapture(virtAddr, buffer->GetSize());
                    }
                    surface->ReleaseBuffer(buffer);
                }
                delete surface;
            }
        }
        delete &fc;
    }


SampleCameraStateMng::~SampleCameraStateMng()
{
    CloseRecorder();
}

void SampleCameraStateMng::OnCreated(Camera &c) 
{
    SAMPLE_INFO("Sample recv OnCreate camera.");
    auto config = CameraConfig::CreateCameraConfig();
    config->SetFrameStateCallback(&fsCb_, &eventHdlr_);
    c.Configure(*config);
    cam_ = &c;
}

void SampleCameraStateMng::OnCreateFailed(const std::string cameraId, int32_t errorCode){}
void SampleCameraStateMng::OnReleased(Camera &c){}

void SampleCameraStateMng::CloseRecorder()
{
    if (recorder_ != nullptr) {
        recorder_->Stop(true);
        recorder_->Release();
        delete recorder_;
        recorder_ = nullptr;
    }
    if (recordFd_ != -1) {
        close(recordFd_);
        recordFd_ = -1;
    }
}

int SampleCameraStateMng::PrepareRecorder()
{
    if (cam_ == nullptr) {
        SAMPLE_ERROR("Camera is not ready.");
        return -1;
    }
    if (recorder_ == nullptr) {
        recorder_ = SampleCreateRecorder();
    }
    if (recorder_ == nullptr) {
        SAMPLE_ERROR("Recorder not available.");
        return -1;
    }
    if (recordFd_ == -1) {
        recordFd_ = SampleGetRecordFd();
    }
    if (recordFd_ == -1) {
        SAMPLE_ERROR("Create fd failed.");
        return -1;
    }
    return SUCCESS;
}

void SampleCameraStateMng::StartRecord()
{
    if (recordState_ == STATE_RUNNING) {
        SAMPLE_INFO("Camera is already recording.");
        return;
    }
    int ret = PrepareRecorder();
    if (ret != SUCCESS) {
        SAMPLE_ERROR("PrepareRecorder failed.");
        CloseRecorder();
        return;
    }
    ret = recorder_->SetOutputFile(recordFd_);
    if (ret != SUCCESS) {
        SAMPLE_ERROR("SetOutputPath failed. ret=%d", ret);
        CloseRecorder();
        return;
    }
    ret = recorder_->Prepare();
    if (ret != SUCCESS) {
        SAMPLE_ERROR("Prepare failed. ret=%d", ret);
        CloseRecorder();
        return;
    }
    ret = recorder_->Start();
    if (ret != SUCCESS) {
        SAMPLE_ERROR("recorder start failed. ret=%d", ret);
        CloseRecorder();
        return;
    }
    FrameConfig *fc = new FrameConfig(FRAME_CONFIG_RECORD);
    Surface *surface = (recorder_->GetSurface(0)).get();
    surface->SetWidthAndHeight(1920, 1080);
    surface->SetQueueSize(3);
    surface->SetSize(1024 * 1024);
    fc->AddSurface(*surface);
    ret = cam_->TriggerLoopingCapture(*fc);
    if (ret != 0) {
        delete fc;
        CloseRecorder();
        SAMPLE_ERROR("camera start recording failed. ret=%d", ret);
        return;
    }
    recordState_ = STATE_RUNNING;
    SAMPLE_INFO("camera start recording succeed.");
}

void SampleCameraStateMng::StartPreview()
{
    if (cam_ == nullptr) {
        SAMPLE_ERROR("Camera is not ready.");
        return;
    }
    if (previewState_ == STATE_RUNNING) {
        SAMPLE_ERROR("Camera is already previewing.");
        return;
    }
    FrameConfig *fc = new FrameConfig(FRAME_CONFIG_PREVIEW);
    Surface *surface = Surface::CreateSurface();
    if (surface == nullptr) {
        delete fc;
        SAMPLE_ERROR("CreateSurface failed");
        return;
    }
    surface->SetWidthAndHeight(1920, 1080); /* 1920:width,1080:height */
    surface->SetUserData("region_position_x", "0");
    surface->SetUserData("region_position_y", "0");
    surface->SetUserData("region_width", "480");
    surface->SetUserData("region_height", "480");
    fc->AddSurface(*surface);
    int32_t ret = cam_->TriggerLoopingCapture(*fc);
    if (ret != 0) {
        delete fc;
        SAMPLE_ERROR("camera start preview failed. ret=");
        return;
    }
    delete surface;
    previewState_ = STATE_RUNNING;
    SAMPLE_INFO("camera start preview succeed.");
}
    
void SampleCameraStateMng::Capture()
{
    if (cam_ == nullptr) {
        SAMPLE_ERROR("Camera is not ready.");
        return;
    }
    FrameConfig *fc = new FrameConfig(FRAME_CONFIG_CAPTURE);
    Surface *surface = Surface::CreateSurface();
    if (surface == nullptr) {
        delete fc;
        SAMPLE_ERROR("CreateSurface failed");
        return;
    }
    surface->SetWidthAndHeight(1920, 1080); /* 1920:width,1080:height */
    fc->AddSurface(*surface);
    cam_->TriggerSingleCapture(*fc);
}
void SampleCameraStateMng::Stop()
{
    if (cam_ == nullptr) {
        SAMPLE_ERROR("Camera is not ready.");
        return;
    }
    cam_->StopLoopingCapture();
    if (recordState_ == STATE_RUNNING) {
        CloseRecorder();
    }
    recordState_ = STATE_IDLE;
    previewState_ = STATE_IDLE;
}

